//---------------------------------------------------------------------------
// cpu.cpp - dead6502v4 v0.98 - 2A03 emulator - copyright 2000-2008 Dead_Body
//
// cycle accurate 2a03 emulator, also very slow
//
// modified for breemlib
//---------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "StdAfx.h"
#include "cpu.h"

//#define UNDOC							//use the undocumented opcodes

//? ???
#undef ABSOLUTE
#undef IN
//? end ???

#pragma warning(disable:4244)		//i know what i am doing!
//---------------------------------------------------------------------------
#define FLAG_C       0x01
#define FLAG_Z       0x02
#define FLAG_I       0x04
#define FLAG_D       0x08
#define FLAG_B       0x10
#define FLAG_X       0x20
#define FLAG_V       0x40
#define FLAG_N       0x80

#define SET(ff)		f |= ff;
#define CLEAR(ff)    f &= ~(ff);

#define EA           eaddr
#define EC           extracycle

#define READ(aa)     MemRead(aa)
#define WRITE(aa,dd) MemWrite(aa,dd);

#define READWORD(aa)    (READ(aa) | (READ((aa)+1)<<8))

#define PUSH(b)\
	{\
	int na = (u32)(s-- | 0x100);\
	MemWrite(na,(u8)(b));\
	}
#define POP(b)\
	{\
	int na = (u32)(++s | 0x100);\
	(b) = MemRead(na);\
	}

#define EA_FETCHLO   EA = (EA & 0xff00) | OpRead(pc++);
#define EA_FETCHHI   EA = (EA & 0x00ff) | (OpRead(pc++) << 8);
#define EA_FETCHLONOHI  EA = OpRead(pc++);
#define TMP_READ     data = READ(EA);
#define TMP_WRITE    WRITE(EA,data);
#define LASTCYCLE    {opcycle = 0;}
#define SET_FLAG_C   f |= 1;
#define CLEAR_FLAG_C f &= ~1;
#define SET_FLAG_Z   f |= 2;
#define CLEAR_FLAG_Z f &= ~2;
#define SET_FLAG_N   f |= 0x80;
#define CLEAR_FLAG_N f &= ~0x80;
#define CHECK_NZ(dd) \
	if((u8)(dd) == 0)	{SET(FLAG_Z);}    \
	else					{CLEAR(FLAG_Z);}  \
	if((dd) & 0x80)	{SET(FLAG_N);}    \
	else					{CLEAR(FLAG_N);}

#include "cpuops.cpp"

#ifndef UNDOC
char *opcodes[256] =
	{
	"brk","ora","???","???","???","ora","asl","???", //00-07
	"php","ora","asl","???","???","ora","asl","???", //08-0f
	"bpl","ora","???","???","???","ora","asl","???", //10-17
	"clc","ora","???","???","???","ora","asl","???", //18-1f
	"jsr","and","???","???","bit","and","rol","???", //20-27
	"plp","and","rol","???","bit","and","rol","???", //28-2f
	"bmi","and","???","???","???","and","rol","???", //30-27
	"sec","and","???","???","???","and","rol","???", //38-3f
	"rti","eor","???","???","???","eor","lsr","???", //40-37
	"pha","eor","lsr","???","jmp","eor","lsr","???", //48-4f
	"bvc","eor","???","???","???","eor","lsr","???", //50-47
	"cli","eor","???","???","???","eor","lsr","???", //58-5f
	"rts","adc","???","???","???","adc","ror","???", //60-57
	"pla","adc","ror","???","jmp","adc","ror","???", //68-6f
	"bvs","adc","???","???","???","adc","ror","???", //70-67
	"sei","adc","???","???","???","adc","ror","???", //78-7f
	"???","sta","???","???","sty","sta","stx","???", //80-87
	"dey","???","txa","???","sty","sta","stx","???", //88-8f
	"bcc","sta","???","???","sty","sta","stx","???", //90-97
	"tya","sta","txs","???","???","sta","???","???", //98-9f
	"ldy","lda","ldx","???","ldy","lda","ldx","???", //a0-a7
	"tay","lda","tax","???","ldy","lda","ldx","???", //a8-af
	"bcs","lda","???","???","ldy","lda","ldx","???", //b0-b7
	"clv","lda","tsx","???","ldy","lda","ldx","???", //b8-bf
	"cpy","cmp","???","???","cpy","cmp","dec","???", //c0-c7
	"iny","cmp","dex","???","cpy","cmp","dec","???", //c8-cf
	"bne","cmp","???","???","???","cmp","dec","???", //d0-d7
	"cld","cmp","???","???","???","cmp","dec","???", //d8-df
	"cpx","sbc","???","???","cpx","sbc","inc","???", //e0-e7
	"inx","sbc","nop","???","cpx","sbc","inc","???", //e8-ef
	"beq","sbc","???","???","???","sbc","inc","???", //f0-f7
	"sed","sbc","???","???","???","sbc","inc","???"  //f8-ff
	};
#else
char *opcodes[256] =
	{
	"brk","ora","???","???","???","ora","asl","???", //00-07
	"php","ora","asl","???","???","ora","asl","???", //08-0f
	"bpl","ora","???","???","???","ora","asl","???", //10-17
	"clc","ora","???","???","???","ora","asl","???", //18-1f
	"jsr","and","???","???","bit","and","rol","???", //20-27
	"plp","and","rol","???","bit","and","rol","???", //28-2f
	"bmi","and","???","???","???","and","rol","???", //30-27
	"sec","and","???","???","???","and","rol","???", //38-3f
	"rti","eor","???","???","???","eor","lsr","???", //40-37
	"pha","eor","lsr","???","jmp","eor","lsr","???", //48-4f
	"bvc","eor","???","???","???","eor","lsr","???", //50-47
	"cli","eor","???","???","???","eor","lsr","???", //58-5f
	"rts","adc","???","???","???","adc","ror","???", //60-57
	"pla","adc","ror","???","jmp","adc","ror","???", //68-6f
	"bvs","adc","???","???","???","adc","ror","???", //70-67
	"sei","adc","???","???","???","adc","ror","???", //78-7f
	"???","sta","???","???","sty","sta","stx","???", //80-87
	"dey","???","txa","???","sty","sta","stx","???", //88-8f
	"bcc","sta","???","???","sty","sta","stx","???", //90-97
	"tya","sta","txs","???","???","sta","???","???", //98-9f
	"ldy","lda","ldx","???","ldy","lda","ldx","lax", //a0-a7
	"tay","lda","tax","???","ldy","lda","ldx","???", //a8-af
	"bcs","lda","???","???","ldy","lda","ldx","???", //b0-b7
	"clv","lda","tsx","???","ldy","lda","ldx","???", //b8-bf
	"cpy","cmp","???","???","cpy","cmp","dec","???", //c0-c7
	"iny","cmp","dex","???","cpy","cmp","dec","???", //c8-cf
	"bne","cmp","???","???","???","cmp","dec","???", //d0-d7
	"cld","cmp","???","???","???","cmp","dec","???", //d8-df
	"cpx","sbc","???","???","cpx","sbc","inc","???", //e0-e7
	"inx","sbc","nop","???","cpx","sbc","inc","???", //e8-ef
	"beq","sbc","???","???","???","sbc","inc","???", //f0-f7
	"sed","sbc","???","???","???","sbc","inc","???"  //f8-ff
	};
#endif

char *Ccpu::GetOpcode(u8 opcode)
{
return(opcodes[opcode]);
}

int Ccpu::GetAddrMode(u8 opcode)
{
switch(opcode & 0x1f)
	{
	case 0x00:
	case 0x02:
		if(opcode == 0x20)
			return(a_absolute);
		switch(opcode & 0x82)
			{
			case 0x00:
				return(a_implied);
			case 0x80:
			case 0x82:
				return(a_immediate);
			}
		break;
	case 0x01:
	case 0x03:
		return(a_indirectx);
	case 0x04:
	case 0x05:
	case 0x06:
	case 0x07:
		return(a_zeropage);
	case 0x08:
		return(a_implied);
	case 0x09:
	case 0x0b:
		return(a_immediate);
	case 0x0a:
		if(opcode & 0x80)
			return(a_implied);
		return(a_accumulator);
	case 0x0c:
	case 0x0d:
	case 0x0e:
	case 0x0f:
		return(a_absolute);
	case 0x10:
		return(a_relative);
	case 0x11:
	case 0x13:
		return(a_indirecty);
	case 0x14:
	case 0x15:
	case 0x16:
	case 0x17:
		return(a_zeropagex);
	case 0x18:
	case 0x1a:
		return(a_implied);
	case 0x19:
	case 0x1b:
		return(a_absolutey);
	case 0x1c:
	case 0x1d:
	case 0x1e:
	case 0x1f:
		return(a_absolutex);
	}
return(a_none);
}

int Ccpu::Disassemble(char *buf,u32 addr)
{
u8 opcode = MemRead(addr);
char *op = GetOpcode(opcode);
char *b = buf;
int ret = addr + 1;

sprintf(buf,"($%02X)\t",opcode);
b += strlen(buf);
switch(GetAddrMode(opcode))
	{
	default:
		sprintf(b,"$%02X",opcode);
		break;
	case a_relative:
		sprintf(b,"%s $%04X",op,addr+(signed char)READ(addr+1)+2);
		ret = addr + 2;
		break;
	case a_immediate:
		sprintf(b,"%s $%02X   ",op,READ(addr+1));
		ret = addr + 2;
		break;
	case a_implied:
		sprintf(b,"%s         ",op);
		ret = addr + 1;
		break;
	case a_accumulator:
		sprintf(b,"%s a       ",op);
		ret = addr + 1;
		break;
	case a_absolute:
		sprintf(b,"%s $%04X",op,READWORD(addr+1));
		ret = addr + 3;
		break;
	case a_absolutex:
		sprintf(b,"%s $%04X,x",op,READWORD(addr+1));
		ret = addr + 3;
		break;
	case a_absolutey:
		sprintf(b,"%s $%04X,y",op,READWORD(addr+1));
		ret = addr + 3;
		break;
	case a_zeropage:
		sprintf(b,"%s $%02X    ",op,READ(addr+1));
		ret = addr + 2;
		break;
	case a_zeropagex:
		sprintf(b,"%s $%02X,x",op,READ(addr+1));
		ret = addr + 2;
		break;
	case a_indirecty:
		sprintf(b,"%s ($%02X),y",op,READ(addr+1));
		ret = addr + 2;
		break;
	case a_indirectx:
		sprintf(b,"%s ($%02X,x)",op,READ(addr+1));
		ret = addr + 2;
		break;
	}
if(debug > 2)
	{
	b += strlen(b);
	sprintf(b,"\tA=$%02X X=$%02X Y=$%02X S=$%02X F=$%02X EA=$%04X",a,x,y,s,f,EA); 
	}
{
char *b = buf;
while(*b)
	{
	*b = toupper(*b);
	b++;
	}
}
return(ret);
}

Ccpu::Ccpu()
{
int i;

for(i=0;i<32;i++)
	{
	ramptr[i] = 0;
	romptr[i] = 0;
	opptr[i] = 0;
	readptr[i] = 0;
	writeptr[i] = 0;
	}
extracycle = 0;
debug = 2;
}

void Ccpu::Reset()
{
badopcode = 0;
opcode = 0;
opcycle = 1;
lastread = lastwrite = 0;
pc = MemRead(0xfffc) | (MemRead(0xfffd) << 8);
s = 0xff;
nmi_active = irq_active = 0;
nmi = irq = 0;
f = 0x24;
cycle = 7;     //7 cycles for a reset...bug!  7 cycles the ppu isnt aware of!
#ifdef NES
ppu->Run(7*3);
#endif
}

void Ccpu::NMI()
{
nmi = 1;
}

void Ccpu::IRQ()
{
irq = 1;
}

void Ccpu::Execute(int ncycles)        //execute n cycles
{
int n;

for(n=0;n<ncycles;n++)
	Step();
}

void Ccpu::Step()
{
if(nmi && opcycle == 1)				//should nmi/irq interrupt opcodes?
	{
	nmi_active = 1;
	nmi = 0;
	}
if(irq && opcycle == 1)
	{
	irq_active = 1;
	irq = 0;
	}
if(nmi_active)
	{
	switch(nmi_active)
		{
		case 1:	READ(pc);			break;
		case 2:	READ(pc+1);			break;
		case 3:	PUSH(pc >> 8);		break;
		case 4:	PUSH(pc);			break;
		case 5:	f &= ~(FLAG_B | FLAG_D);	PUSH(f);	break;
		case 6:	pc = MemRead(0xfffa);	break;
		case 7:	pc |= MemRead(0xfffb) << 8;	nmi_active = 0;	opcycle = 1;	break;
		}
	if(nmi_active)
		nmi_active++;
	cycle++;
	return;
	}
if(irq_active)
	{
	switch(irq_active)
		{
		case 1:	READ(pc);			break;
		case 2:	READ(pc+1);			break;
		case 3:	PUSH(pc >> 8);		break;
		case 4:	PUSH(pc);			break;
		case 5:	PUSH(f | FLAG_I);	break;
		case 6:	pc = READ(0xfffe);break;
		case 7:	pc |= READ(0xffff) << 8;	irq_active = 0;	opcycle = 1;	break;
		}
	if(irq_active)
		irq_active++;
	cycle++;
	return;
	}
if(opcycle == 1)                       //time for new opcode, all first cycles
	{                                   //just read the opcode
	opcode = OpRead(pc++);
/*	if(debug > 1)
		{
		char ss[512];

		sprintf(ss,"%s\n",Disassemble(dbuf,opcode,pc-1));
		message(ss);
		}*/
	opcycle++;
	}
else                                   //stepping thru an opcode already
	{                                   //we are on 2nd cycle...
	switch(opcode)
		{
		default:
			message("bad opcode at $%04X (opcode = $%02X, cycle = %d)\n",pc-1,opcode,cycle);
			badopcode++;
			cycle++;
			return;
		case 0x00:	//BRK
			switch(opcycle)
				{
				case 2:
					READ(pc++);
					break;
				case 3:
					PUSH(pc >> 8);
					f |= FLAG_B;
					break;
				case 4:
					PUSH(pc);
					break;
				case 5:
					PUSH(f | FLAG_B);
					break;
				case 6:
					/*if(nmi)
						pc = READ(0xfffa);
					else*/
						pc = READ(0xfffe);
					break;
				case 7:
					/*if(nmi)
						{
						pc |= READ(0xfffb) << 8;
						nmi = 0;
						}
					else*/
						pc |= READ(0xffff) << 8;
					break;
				}
			break;
		case 0x02:
			if(opcycle == 2)
				{
				char ss[256],*s = ss;
				u16 aa,nn;

				aa = READ(0) | (READ(1) << 8);
				for(nn=0;nn<256;nn++)
					ss[nn] = 0;
				while((nn = READ(aa++)) != 0)
					*s++ = (u8)nn;
				message("NESMSG from $%04X: \n",READ(0) | (READ(1) << 8));
				message("  %s\n",ss);
				LASTCYCLE;
				}
			break;
		case 0x6C:                       //jmp
			if(opcycle == 2)  {EA_FETCHLO;}
			else if(opcycle == 3)  {EA_FETCHHI;}
			else if(opcycle == 4)  data = READ(EA);
			else if(opcycle == 5)
				{
				pc = data | (READ(((EA + 1) & 0xFF) | (EA & 0xFF00)) << 8);
				LASTCYCLE;
				}
			break;
		case 0x20:                       //jsr
			if(opcycle == 2)  data = READ(pc++);
			else if(opcycle == 4){PUSH(pc >> 8);}
			else if(opcycle == 5){PUSH(pc & 0xff);}
			else if(opcycle == 6)
				{
				pc = data | (READ(pc) << 8);
				LASTCYCLE;
				}
			break;
		case 0x60:                       //rts
			if(opcycle == 2)  READ(pc);
			else if(opcycle == 3)  s++;
			else if(opcycle == 4)  pc = READ(s++ | 0x100);
			else if(opcycle == 5)  pc |= READ(s | 0x100) << 8;
			else if(opcycle == 6)
				{
				pc++;
				LASTCYCLE;
				}
			break;
		case 0x40:                       //rti 
			if(opcycle == 2)  READ(pc);
			else if(opcycle == 3)  s++;
			else if(opcycle == 4)  f = READ(s++ | 0x100);
			else if(opcycle == 5)	pc = READ(s++ | 0x100);
			else if(opcycle == 6)
				{
				pc |= READ(s | 0x100) << 8;
				LASTCYCLE;
				}
			break;
		case 0x08:                       //php
			if(opcycle == 2)  READ(pc);
			else if(opcycle == 3)
				{
				PUSH(f | FLAG_B);
				LASTCYCLE;
				}
			break;
		case 0x48:                       //pha
			if(opcycle == 2)  READ(pc);
			else if(opcycle == 3)
				{
				PUSH(a);
				LASTCYCLE;
				}
			break;
		case 0x28:                       //plp
			if(opcycle == 2)  READ(pc);
			else if(opcycle == 3)  s++;
			else if(opcycle == 4)
				{
				f = READ(s | 0x100) | 0x20;
				LASTCYCLE;
				}
			break;
		case 0x68:                       //pla
			if(opcycle == 2)  READ(pc);
			else if(opcycle == 3)  s++;
			else if(opcycle == 4)
				{
				a = READ(s | 0x100);
				CHECK_NZ(a);
				LASTCYCLE;
				}
			break;
		case 0x4C:                       //jmp abs (3 cycles)
			if(opcycle == 2) EA_FETCHLO;  //fetch address low
			if(opcycle == 3)        //copy low byte, fetch address high+copy
				{
				EA_FETCHHI;
				pc = eaddr;
				LASTCYCLE;
				}
			break;
		case 0x4A:                       //lsr a
			if(opcycle == 2)              //write data back and rotate
				{
				if(a & 1)   {SET_FLAG_C;}
				else           {CLEAR_FLAG_C;}
				a >>= 1;
				CHECK_NZ(a);
				LASTCYCLE;
				}
			break;
		case 0x0A:                       //asl a
			if(opcycle == 2)
				{
				if(a & 0x80)
					{SET(FLAG_C);}
				else
					{CLEAR(FLAG_C);}
				a <<= 1;
				CHECK_NZ(a);
				LASTCYCLE;
				}
			break;
		case 0x2A:		//rol a
			if(opcycle == 2)
				{
				u8 t = a & 0x80;

				a <<= 1;       
				a |= f & 1;    
				CLEAR(FLAG_C);    
				f |= t >> 7;
				CHECK_NZ(a);      
				LASTCYCLE;
				}
			break;
		case 0x6A:		//ror a
			if(opcycle == 2)
				{
				u8 t = a & 1;

				a >>= 1;       
				a |= f << 7;  
				CLEAR(FLAG_C);    
				f |= t;
				CHECK_NZ(a);      
				LASTCYCLE;
				}
			break;
//dec
		case 0xC6:ZEROPAGE;DEC(3);break;
		case 0xD6:ZEROPAGEX;DEC(4);break;
		case 0xCE:ABSOLUTE;DEC(4);break;
		case 0xDE:ABSOLUTEX;DEC(5);break;
//inc
		case 0xE6:ZEROPAGE;INC(3);break;
		case 0xF6:ZEROPAGEX;INC(4);break;
		case 0xEE:ABSOLUTE;INC(4);break;
		case 0xFE:ABSOLUTEX;INC(5);break;
//dex/dey
		case 0x88:DE(y);break;           //dey
		case 0xCA:DE(x);break;           //dex
//inx/iny
		case 0xC8:IN(y);break;           //iny
		case 0xE8:IN(x);break;           //inx
//branches
		case 0x10:B((f & FLAG_N) == 0);break;  //bpl
		case 0x30:B(f & FLAG_N);break;         //bmi
		case 0x50:B((f & FLAG_V) == 0);break;  //bvc
		case 0x70:B(f & FLAG_V);break;         //bvs
		case 0x90:B((f & FLAG_C) == 0);break;  //bcc
		case 0xB0:B(f & FLAG_C);break;         //bcs
		case 0xD0:B((f & FLAG_Z) == 0);break;  //bne
		case 0xF0:B(f & FLAG_Z);break;         //beq
//transfer operations
		case 0x8A:T(x,a);break;                //txa
		case 0x98:T(y,a);break;                //tya
		case 0x9A:if(opcycle == 2){s = x;LASTCYCLE;}break;     //txs
		case 0xA8:T(a,y);break;                //tay
		case 0xAA:T(a,x);break;                //tax
		case 0xBA:T(s,x);break;                //tsx
//load
		case 0xA0:LD(2,y,pc++);break;             //ldy immediate (2 cycle)
		case 0xA2:LD(2,x,pc++);break;             //ldx immediate (2 cycle)
		case 0xA9:LD(2,a,pc++);break;             //lda immediate (2 cycle)
		case 0xA4:ZEROPAGE;LD(3,y,EA);break;      //ldy zp
		case 0xA5:ZEROPAGE;LD(3,a,EA);break;      //lda zp
		case 0xA6:ZEROPAGE;LD(3,x,EA);break;      //ldx zp
		case 0xAC:ABSOLUTE;LD(4,y,EA);break;      //ldy absolute
		case 0xAD:ABSOLUTE;LD(4,a,EA);break;      //lda absolute
		case 0xAE:ABSOLUTE;LD(4,x,EA);break;      //ldx absolute
		case 0xA1:INDIRECTX;LD(6,a,EA);break;		//lda (,x)
		case 0xB1:IND_IDX_R;LD(5+EC,a,EA);break;  //lda (),y
		case 0xB4:ZEROPAGEX;LD(4,y,EA);break;     //ldy zp,x
		case 0xB5:ZEROPAGEX;LD(4,a,EA);break;     //lda zp,x
		case 0xB6:ZEROPAGEY;LD(4,x,EA);break;     //ldx zp,y
		case 0xBC:ABSOLUTEX_R;LD(4+EC,y,EA);break;//ldy ab,x
		case 0xBD:ABSOLUTEX_R;LD(4+EC,a,EA);break;//lda ab,x
		case 0xBE:ABSOLUTEY_R;LD(4+EC,x,EA);break;//ldx ab,y
		case 0xB9:ABSOLUTEY_R;LD(4+EC,a,EA);break;//lda ab,y
//store
		case 0x84:ZEROPAGE;ST(3,y,EA);break;	//sty zp
		case 0x85:ZEROPAGE;ST(3,a,EA);break;   //sta zp
		case 0x86:ZEROPAGE;ST(3,x,EA);break;   //stx zp
		case 0x8C:ABSOLUTE;ST(4,y,EA);break;   //sty abs
		case 0x8D:ABSOLUTE;ST(4,a,EA);break;   //sta abs
		case 0x8E:ABSOLUTE;ST(4,x,EA);break;   //stx abs
		case 0x81:INDIRECTX;ST(6,a,EA);break;  //sta indirect indexed
		case 0x91:IND_IDX;ST(6,a,EA);break;    //sta indirect indexed
		case 0x94:ZEROPAGEX;ST(4,y,EA);break;  //sty zp,x
		case 0x95:ZEROPAGEX;ST(4,a,EA);break;  //sta zp,x
		case 0x96:ZEROPAGEY;ST(4,x,EA);break;  //stx zp,y
		case 0x99:ABSOLUTEY;ST(5,a,EA);break;  //sta abs,y
		case 0x9D:ABSOLUTEX;ST(5,a,EA);break;  //sta abs,x
//ora
		case 0x09:ORA(2,pc++);break;
		case 0x05:ZEROPAGE;ORA(3,EA);break;
		case 0x15:ZEROPAGEX;ORA(3,EA);break;
		case 0x0D:ABSOLUTE;ORA(4,EA);break;
		case 0x1D:ABSOLUTEX_R;ORA(4+EC,EA);break;
		case 0x19:ABSOLUTEY_R;ORA(4+EC,EA);break;
		case 0x01:INDIRECTX;ORA(6,EA);break;
		case 0x11:IND_IDX_R;ORA(5+EC,EA);break;
//and
		case 0x29:AND(2,pc++);break;
		case 0x25:ZEROPAGE;AND(3,EA);break;
		case 0x35:ZEROPAGEX;AND(3,EA);break;
		case 0x2D:ABSOLUTE;AND(4,EA);break;
		case 0x3D:ABSOLUTEX_R;AND(4+EC,EA);break;
		case 0x39:ABSOLUTEY_R;AND(4+EC,EA);break;
		case 0x21:INDIRECTX;AND(6,EA);break;
		case 0x31:IND_IDX_R;AND(5+EC,EA);break;
//eor
		case 0x49:EOR(2,pc++);break;
		case 0x45:ZEROPAGE;EOR(3,EA);break;
		case 0x55:ZEROPAGEX;EOR(3,EA);break;
		case 0x4D:ABSOLUTE;EOR(4,EA);break;
		case 0x5D:ABSOLUTEX_R;EOR(4+EC,EA);break;
		case 0x59:ABSOLUTEY_R;EOR(4+EC,EA);break;
		case 0x41:INDIRECTX;EOR(6,EA);break;
		case 0x51:IND_IDX_R;EOR(5+EC,EA);break;
//bit
		case 0x24:ZEROPAGE;BIT(3);break;
		case 0x2C:ABSOLUTE;BIT(4);break;
//adc
		case 0x69:EA = pc++;ADC(2);break;
		case 0x65:ZEROPAGE;ADC(3);break;
		case 0x75:ZEROPAGEX;ADC(4);break;
		case 0x6D:ABSOLUTE;ADC(4);break;
		case 0x7D:ABSOLUTEX_R;ADC(4+EC);break;
		case 0x79:ABSOLUTEY_R;ADC(4+EC);break;
		case 0x61:INDIRECTX;ADC(6);break;
		case 0x71:IND_IDX_R;ADC(5+EC);break;
//sbc
		case 0xE9:EA = pc++;SBC(2);break;
		case 0xE5:ZEROPAGE;SBC(3);break;
		case 0xF5:ZEROPAGEX;SBC(4);break;
		case 0xED:ABSOLUTE;SBC(4);break;
		case 0xFD:ABSOLUTEX_R;SBC(4+EC);break;
		case 0xF9:ABSOLUTEY_R;SBC(4+EC);break;
		case 0xE1:INDIRECTX;SBC(6);break;
		case 0xF1:IND_IDX_R;SBC(5+EC);break;
//asl
		case 0x06:ZEROPAGE;ASL(3);break;
		case 0x16:ZEROPAGEX;ASL(4);break;
		case 0x0E:ABSOLUTE;ASL(4);break;
		case 0x1E:ABSOLUTEX;ASL(5);break;
//lsr
		case 0x46:ZEROPAGE;LSR(3);break;
		case 0x56:ZEROPAGEX;LSR(4);break;
		case 0x4E:ABSOLUTE;LSR(4);break;
		case 0x5E:ABSOLUTEX;LSR(5);break;
//rol
		case 0x26:ZEROPAGE;ROL(3);break;
		case 0x36:ZEROPAGEX;ROL(4);break;
		case 0x2E:ABSOLUTE;ROL(4);break;
		case 0x3E:ABSOLUTEX;ROL(5);break;
//ror
		case 0x66:ZEROPAGE;ROR(3);break;
		case 0x76:ZEROPAGEX;ROR(4);break;
		case 0x6E:ABSOLUTE;ROR(4);break;
		case 0x7E:ABSOLUTEX;ROR(5);break;
//cmp
		case 0xC9:CMP(2,a,pc++);break;					//cmp imm
		case 0xC5:ZEROPAGE;CMP(3,a,EA);break;			//cmp zp
		case 0xD5:ZEROPAGEX;CMP(4,a,EA);break;			//cmp zp,x
		case 0xCD:ABSOLUTE;CMP(4,a,EA);break;			//cmp abs
		case 0xDD:ABSOLUTEX_R;CMP(4+EC,a,EA);break;  //cmp abs,x
		case 0xD9:ABSOLUTEY_R;CMP(4+EC,a,EA);break;	//cmp abs,y
		case 0xC1:INDIRECTX;CMP(6,a,EA);break;			//cmp ind,x
		case 0xD1:IND_IDX_R;CMP(5+EC,a,EA);break;    //cmp ind,y
//cpx
		case 0xE0:CMP(2,x,pc++);break;					//cpx imm
		case 0xE4:ZEROPAGE;CMP(3,x,EA);break;			//cpx zp
		case 0xEC:ABSOLUTE;CMP(4,x,EA);break;			//cpx abs
//cpy
		case 0xC0:CMP(2,y,pc++);break;					//cpy imm
		case 0xC4:ZEROPAGE;CMP(3,y,EA);break;			//cpy zp
		case 0xCC:ABSOLUTE;CMP(4,y,EA);break;			//cpy abs
//clear flag
		case 0x18:CL(FLAG_C);break;      //clc
		case 0x58:CL(FLAG_I);break;      //cli
		case 0xB8:CL(FLAG_V);break;      //clv
		case 0xD8:CL(FLAG_D);break;      //cld
//set flag
		case 0x38:SE(FLAG_C);break;      //sec
		case 0x78:SE(FLAG_I);break;      //sei
		case 0xF8:SE(FLAG_D);break;      //sed
//nop
		case 0xEA:NOP(2);break;				//nop
#ifdef UNDOC
//undocumented
//nop
		case 0x1A:NOP(2);break;
		case 0x3A:NOP(2);break;
		case 0x5A:NOP(2);break;
		case 0x7A:NOP(2);break;
		case 0xDA:NOP(2);break;
		case 0xFA:NOP(2);break;
//dop
		case 0x04:ZEROPAGE;NOP(3);break;
		case 0x14:ZEROPAGEX;NOP(4);break;
		case 0x34:ZEROPAGEX;NOP(4);break;
		case 0x44:ZEROPAGE;NOP(3);break;
		case 0x54:ZEROPAGEX;NOP(4);break;
		case 0x64:ZEROPAGE;NOP(3);break;
		case 0x74:ZEROPAGEX;NOP(4);break;
		case 0x80:NOP(2);break;
		case 0x82:NOP(2);break;
		case 0x89:NOP(2);break;
		case 0xC2:NOP(2);break;
		case 0xD4:ZEROPAGEX;NOP(4);break;
		case 0xE2:NOP(2);break;
		case 0xF4:ZEROPAGEX;NOP(4);break;
//top
		case 0x0C:ABSOLUTE;NOP(4);break;
		case 0x1C:ABSOLUTEX_R;NOP(4+EC);break;
		case 0x3C:ABSOLUTEX_R;NOP(4+EC);break;
		case 0x5C:ABSOLUTEX_R;NOP(4+EC);break;
		case 0x7C:ABSOLUTEX_R;NOP(4+EC);break;
		case 0xDC:ABSOLUTEX_R;NOP(4+EC);break;
		case 0xFC:ABSOLUTEX_R;NOP(4+EC);break;
//lax
		case 0xA7:ZEROPAGE;LAX(3);break;
		case 0xB7:ZEROPAGEY;LAX(4);break;
		case 0xAF:ABSOLUTE;LAX(4);break;
		case 0xBF:ABSOLUTEY_R;LAX(4+EC);break;
		case 0xA3:INDIRECTX;LAX(6);break;
		case 0xB3:IND_IDX_R;LAX(5+EC);break;
//sax
		case 0x87:ZEROPAGE;SAX(3);break;
		case 0x97:ZEROPAGEY;SAX(4);break;
		case 0x83:INDIRECTX;SAX(6);break;
		case 0x8F:ABSOLUTE;SAX(4);break;
//dcp
		case 0xC7:ZEROPAGE;DCP(3);break;
		case 0xD7:ZEROPAGEX;DCP(4);break;
		case 0xCF:ABSOLUTE;DCP(4);break;
		case 0xDF:ABSOLUTEX;DCP(5);break;
		case 0xDB:ABSOLUTEY;DCP(5);break;
		case 0xC3:INDIRECTX;DCP(6);break;
		case 0xD3:IND_IDX;DCP(6);break;
//isb
		case 0xE7:ZEROPAGE;ISB(3);break;
		case 0xF7:ZEROPAGEX;ISB(4);break;
		case 0xEF:ABSOLUTE;ISB(4);break;
		case 0xFF:ABSOLUTEX;ISB(5);break;
		case 0xFB:ABSOLUTEY;ISB(5);break;
		case 0xE3:INDIRECTX;ISB(5);break;
		case 0xF3:IND_IDX;ISB(6);break;
//sbc
		case 0xEB:EA = pc++;SBC(2);break;
//slo
		case 0x07:ZEROPAGE;SLO(3);break;
		case 0x17:ZEROPAGEX;SLO(4);break;
		case 0x0F:ABSOLUTE;SLO(4);break;
		case 0x1F:ABSOLUTEX;SLO(5);break;
		case 0x1B:ABSOLUTEY;SLO(5);break;
		case 0x03:INDIRECTX;SLO(6);break;
		case 0x13:IND_IDX;SLO(6);break;
//rla
		case 0x27:ZEROPAGE;RLA(3);break;
		case 0x37:ZEROPAGEX;RLA(4);break;
		case 0x2F:ABSOLUTE;RLA(4);break;
		case 0x3F:ABSOLUTEX;RLA(5);break;
		case 0x3B:ABSOLUTEY;RLA(5);break;
		case 0x23:INDIRECTX;RLA(6);break;
		case 0x33:IND_IDX;RLA(6);break;
//sre
		case 0x47:ZEROPAGE;SRE(3);break;
		case 0x57:ZEROPAGEX;SRE(4);break;
		case 0x4F:ABSOLUTE;SRE(4);break;
		case 0x5F:ABSOLUTEX;SRE(5);break;
		case 0x5B:ABSOLUTEY;SRE(5);break;
		case 0x43:INDIRECTX;SRE(6);break;
		case 0x53:IND_IDX;SRE(6);break;
//rra
		case 0x67:ZEROPAGE;RRA(3);break;
		case 0x77:ZEROPAGEX;RRA(4);break;
		case 0x6F:ABSOLUTE;RRA(4);break;
		case 0x7F:ABSOLUTEX_R;RRA(5+EC);break;
		case 0x7B:ABSOLUTEY_R;RRA(5+EC);break;
		case 0x63:INDIRECTX;RRA(6);break;
		case 0x73:IND_IDX_R;RRA(6+EC);break;
#endif
		}
	opcycle++;
	}
cycle++;
}

void Ccpu::ExecuteOpcode()
{
do
	{
	Step();
	} while(opcycle != 1);
}

void Ccpu::EatCycles(int n)
{
cycle += n;
}

#ifdef SAVESTATES
void Ccpu::SaveState(CState *s)
{
s->Write8(a);
s->Write8(x);
s->Write8(y);
s->Write8(this->s);
s->Write8(f);
s->Write32(pc);
s->Write32(eaddr);
s->Write8(data);
s->Write32(tmp);
s->Write8(opcode);
s->Write8(opcycle);
s->Write8(extracycle);
s->Write64(cycle);
s->Write32((u32)nmi);
s->Write32((u32)irq);
s->Write32((u32)nmi_active);
s->Write32((u32)irq_active);
s->Write32(lastread);
s->Write32(lastwrite);
s->Write8(r4016reads);
s->Write8(r4017reads);
s->Write8(strobe);
}

void Ccpu::LoadState(CState *s)
{
a = s->Read8();
x = s->Read8();
y = s->Read8();
this->s = s->Read8();
f = s->Read8();
pc = s->Read32();
eaddr = s->Read32();
data = s->Read8();
tmp = s->Read32();
opcode = s->Read8();
opcycle = s->Read8();
extracycle = s->Read8();
cycle = s->Read64();
nmi = (int)s->Read32();
irq = (int)s->Read32();
nmi_active = (int)s->Read32();
irq_active = (int)s->Read32();
lastread = s->Read32();
lastwrite = s->Read32();
r4016reads = s->Read8();
r4017reads = s->Read8();
strobe = s->Read8();
}

#endif
u8 Ccpu::OpRead(u32 addr)
{
lastread = addr;
#ifdef NES
if(addr < 0x8000)
	{
	switch(addr & 0xe000)
		{
		case 0x0000:                     //ram read
			if(readptr[addr >> (12-1)] == 0)
				return(romptr[addr >> (12 - 1)][addr & ~0xf800]);
			return(readptr[addr >> (12-1)](addr));
		case 0x2000:                     //ppu read
			if(readptr[addr >> (12-1)] == 0)
				return(ppu->Read(addr & 7));
			return(readptr[addr >> (12-1)](addr));
		case 0x4000:                     //apu read
			if(readptr[addr >> (12-1)] == 0)
				return(apu->Read(addr));
			return(readptr[addr >> (12-1)](addr));
		case 0x6000:                     //sram read
			if(readptr[addr >> (12-1)] == 0)
				{
				if(romptr[addr >> (12 - 1)])
					return(romptr[addr >> (12 - 1)][addr & ~0xf800]);
				return(0);
				}
			return(readptr[addr >> (12-1)](addr));
		}
	}
return(romptr[addr >> (12 - 1)][addr & ~0xf800]);
#else
//todo: finish this
if(opromptr[addr >> 11] != 0)
	return(opromptr[addr >> 11][addr & ~0xf800]);
return(opptr[addr >> 11](user,addr));
#endif
}

u8 Ccpu::MemRead(u32 addr)
{
lastread = addr;
#ifdef NES
u8 ret;

if(addr < 0x8000)
	{
	switch(addr & 0xe000)
		{
		case 0x0000:                     //ram read
			return(romptr[addr >> (12 - 1)][addr & ~0xf800]);
		case 0x2000:                     //ppu read
			if(readptr[addr >> (12-1)] == 0)
				return(ppu->Read(addr & 7));
			return(readptr[addr >> (12-1)](addr));
		case 0x4000:                     //apu read
			//todo: move these...again!
			if(addr == 0x4016)
				{
				ret = (u8)((input->r4016 >> r4016reads) & 1) | 0x40;
				r4016reads++;
				return(ret);				
				}
			else if(addr == 0x4017)
				{
				ret = (u8)((input->r4017 >> r4017reads) & 1) | 0x40;
				r4017reads++;
				return(ret);				
				}
			if(readptr[addr >> (12-1)] == 0)
				return(apu->Read(addr));
			return(readptr[addr >> (12-1)](addr));
		case 0x6000:                     //sram read
			if(readptr[addr >> (12-1)] == 0)
				{
				if(romptr[addr >> (12 - 1)])
					return(romptr[addr >> (12 - 1)][addr & ~0xf800]);
				return(0);
				}
			return(readptr[addr >> (12-1)](addr));
		}
	}
return(romptr[addr >> (12 - 1)][addr & ~0xf800]);
#else
if(romptr[addr >> 11] != 0)
	return(romptr[addr >> 11][addr & ~0xf800]);
return(readptr[addr >> 11](user,addr));
#endif
}

void Ccpu::MemWrite(u32 addr,u8 data)
{
lastwrite = addr;
#ifdef NES
if(addr < 0x8000)
	{
	switch(addr & 0xe000)
		{
		case 0x0000:                           //ram write
			ramptr[addr >> (12 - 1)][addr & ~0xf800] = data;
			break;
		case 0x2000:                           //ppu write
			if(writeptr[addr >> (12-1)] == 0)
				ppu->Write(addr & 7,data);
			else
				writeptr[addr >> (12-1)](addr,data);
			break;
		case 0x4000:                           //apu write
			//these two if's need to be somewhere else (possibly with apu?)
			if(addr == 0x4014)						//sprdma write
				{
				int i,t,n = data * 0x100;

				for(i=0;i<256;i++)
					{
					t = MemRead(n++);					//read byte to be written
					cycle++;								//increment cpu cycle counter
					ppu->Run(3);						//each cpu cycle is 3 ppu cycles, so step 3
					ppu->sprmem[ppu->spraddr++] = t;	//write byte
					cycle++;								//increment cpu cycle counter again
					ppu->Run(3);						//step three more ppu cycles
					}
				}
			else if(addr == 0x4016)						//joy1 write
				{
				if(data & 1)
					input->Poll();
				if((data & 1) == 0 && strobe)
					{
					r4016reads = 0;
					r4017reads = 0;
					}
				strobe = data & 1;				
				}
			if(writeptr[addr >> (12-1)] == 0)
				apu->Write(addr,data);
			else
				writeptr[addr >> (12-1)](addr,data);
			break;
		case 0x6000:                           //sram read
			if(writeptr[addr >> (12-1)] == 0)
				{
				if(ramptr[addr >> (12 - 1)])
					ramptr[addr >> (12 - 1)][addr & ~0xf800] = data;
				}
			else
				writeptr[addr >> (12-1)](addr,data);
			break;
		}
	}
else if(writeptr[addr >> (12-1)])
	writeptr[addr >> (12-1)](addr,data);
else
	{
	if(ramptr[addr >> (12 - 1)])
		ramptr[addr >> (12 - 1)][addr & ~0xf800] = data;
	}
#else
if(ramptr[addr >> (12 - 1)])
	ramptr[addr >> (12 - 1)][addr & ~0xf800] = data;
else if(writeptr[addr >> (12-1)])
	writeptr[addr >> (12-1)](user,addr,data);
#endif
}
